<script setup>
import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { useTiandituLoader } from '../composables/useTiandituLoader.js'
import baseConfig from '../lib/defaultConfig.js'
import TdtMapCore from '../lib/TdtMapCore.js'
import { deepClone, deepMerge } from '../utils/merge.js'

const props = defineProps({
  config: {
    type: Object,
    default: () => ({}),
  },
  sourceData: {
    type: Array,
    default: () => [],
  },
  apis: {
    type: Object,
    default: () => ({}),
  },
})

const emit = defineEmits(['markerClick', 'ready', 'error'])

const rootRef = ref(null)

const state = reactive({
  core: null,
  loader: null,
  visible: true,
})

const mergedConfig = computed(() => {
  const merged = deepMerge(deepClone(baseConfig), props.config || {})
  merged.apis = props.apis || {}
  return merged
})

const currentKey = computed(() => mergedConfig.value?.tdtKey || baseConfig.tdtKey)

const ensureLoader = () => {
  if (!state.loader || state.loader.key !== currentKey.value) {
    const loader = useTiandituLoader(currentKey.value)
    state.loader = { ...loader, key: currentKey.value }
  }
  return state.loader
}

const attachCoreEvents = () => {
  if (!state.core) return
  state.core.on('markerClick', (payload) => emit('markerClick', payload))
}

const detachCoreEvents = () => {
  if (!state.core) return
  state.core.off('markerClick')
}

const renderData = (data, config) => {
  if (!state.core) return
  const payload = Array.isArray(data) ? deepClone(data) : []
  state.core.render(payload, config)
}

const initMap = async () => {
  const container = rootRef.value
  if (!container) return

  const loader = ensureLoader()
  try {
    await loader.load()
  } catch (error) {
    emit('error', error)
    return
  }

  state.core = new TdtMapCore(container, mergedConfig.value)
  attachCoreEvents()
  renderData(props.sourceData, mergedConfig.value)
  emit('ready')
}

const recreateMap = async () => {
  if (state.core) {
    detachCoreEvents()
    state.core.destroy()
    state.core = null
  }
  await initMap()
}

onMounted(() => {
  initMap()
})

onBeforeUnmount(() => {
  if (state.core) {
    detachCoreEvents()
    state.core.destroy()
    state.core = null
  }
})

watch(
  () => mergedConfig.value,
  async (config, oldConfig) => {
    if (!state.core) {
      await initMap()
      return
    }
    if (config.tdtKey !== oldConfig?.tdtKey) {
      await recreateMap()
      return
    }
    state.core.mergeConfig(config)
    renderData(props.sourceData, config)
  },
  { deep: true }
)

watch(
  () => props.sourceData,
  (data) => {
    renderData(data, mergedConfig.value)
  },
  { deep: true }
)

const flyTo = (fields) => {
  state.core?.flyTo(fields)
}

const show = () => {
  state.visible = true
  state.core?.show()
}

const hide = () => {
  state.visible = false
  state.core?.hide()
}

const resize = (width, height) => {
  state.core?.resize(width, height)
}

const destroy = () => {
  if (state.core) {
    detachCoreEvents()
    state.core.destroy()
    state.core = null
  }
}

const render = (data, config) => {
  if (!state.core) return
  const merged = config ? deepMerge(deepClone(baseConfig), config) : mergedConfig.value
  state.core.mergeConfig(merged)
  renderData(data ?? props.sourceData, merged)
}

defineExpose({
  flyTo,
  show,
  hide,
  resize,
  destroy,
  render,
})
</script>

<template>
  <div ref="rootRef" class="tdt-map-root"></div>
</template>

<style scoped>
.tdt-map-root {
  position: relative;
  width: 100%;
  height: 100%;
  min-height: 520px;
}
</style>
